#!/usr/bin/python3
import argparse
import copy
import csv
import os
import re
from requests.auth import HTTPBasicAuth
from lxml import etree
from base import http
from settings import ACCOUNT, PASSWORD, FOLER_PATH, PROJECT
import subprocess


class OpensuseObsAPI:
    """
    opensuse obs中的相关接口操作
    """

    host = "http://117.78.1.88/"

    def __init__(self, project=None, account=None, password=None) -> None:
        self.un_csv_writer = None
        self._project = project
        self._account = account or ACCOUNT
        self._password = password or PASSWORD
        self.un_packages = dict()
        self.original_un_package = None

    def _url(self, url, **kwargs):
        return self.host + url.format(**kwargs)

    @property
    def _auth(self):
        return HTTPBasicAuth(self._account, self._password)

    @staticmethod
    def _xpath(xpath, node, choice=False):
        try:
            element_result = node.xpath(xpath)
            return element_result if choice else element_result[0]
        except IndexError:
            return None

    def get_unresolvable(self, project_name=None):
        """
        获取某一个工程下的所有unresolvable的包信息
        """
        if not project_name:
            project_name = self._project
        url = self._url(url="build/{project}/_result", project=project_name)
        response = http.get(url, auth=self._auth)

        if response.status_code != 200 and response.text:
            return None
        etree_element = etree.HTML(response.text)
        # self.obs_states.append([project_name, self._parse_arch_state(state)])
        obs_info = list()
        for _result in self._xpath(xpath="//result", node=etree_element, choice=True):
            for status in self._xpath(xpath="./status", node=_result, choice=True):
                build_requires = list()
                build_require = (
                    self._xpath(xpath="./details/text()", node=status)
                    if self._xpath(xpath="./@code", node=status) == "unresolvable"
                    else []
                )
                if build_require:
                    for provides in build_require.split(","):
                        build_requires.append(
                            provides.replace("nothing provides", "").strip()
                        )
                pkgs = dict(
                    architecture=self._xpath(
                        xpath="./@repository", node=_result),
                    repo_name=self._xpath(xpath="./@package", node=status),
                    build_status=self._xpath(xpath="./@code", node=status),
                    obs_branch=project_name,
                    build_requires=build_requires,
                )
                obs_info.append(pkgs)
        return obs_info

    def parse_unresolvable_package(self, project):
        pkgs = self.get_unresolvable(project_name=project)
        if not pkgs:
            raise ValueError("wufa huoqu gongcheng zhuangtai")
        for pkg in pkgs:
            if pkg["build_status"] == "unresolvable":
                pks = list()
                for pacakge in pkg["build_requires"]:
                    if "needed by" in pacakge:
                        pks.append(pacakge.split(" ")[-1])
                    elif "=" in pacakge:
                        pks.append(pacakge.split(" ")[0])
                    else:
                        pks.append(pacakge)
                self.un_packages[pkg["repo_name"]] = list(set(pks))
        return self.un_packages

    @staticmethod
    def provides_source(provide):
        """
        获取组件对应的源码包
        """
        source_package = subprocess.check_output(
            args=["dnf", "repoquery", "--source", "--whatprovides", provide],
            shell=False

        ).decode("utf-8")
        if not source_package:
            return None
        package = re.sub(
            r"(-\d+[.-]?)+([.-]?[A-Za-z0-9]*)*", "", source_package)
        if package.endswith("-"):
            package = package[:-1]
        return package

    def _get_unpackage(self, unpackage):
        return copy.deepcopy(self.un_packages.get(unpackage))

    def _write_csv_update_df_row(
            self,
            lst,
            pkg=None):
        """
        to write csv row and return new csv row list
        Args:
            lst:old csv row list
            res_type:install or build
            names_set:Store a list of package names that have been found
            pkg:package name

        Returns:
            new csv row list
        """
        local_lst = copy.deepcopy(lst)
        csv_writer = self.un_csv_writer
        if not pkg:
            csv_writer.writerow(local_lst)
            return lst
        local_lst.append(pkg)
        csv_writer.writerow(local_lst)
        return lst

    def _data_to_csv(self, pkg_name, names_set):
        # 组件
        components = self._get_unpackage(pkg_name)

        if not components:
            return
        # 组件找源码
        c = [self.provides_source(i).replace("\n", "") for i in
             components if self.provides_source(i)]
        _stack = [c]
        df_row = [pkg_name]
        if not _stack:
            return
        while _stack:
            if not _stack[-1]:
                break
            next_pkg = _stack[-1].pop()
            if not next_pkg:
                continue
            if next_pkg not in df_row:

                names_set.add(next_pkg)
                df_row.append(next_pkg)
                # 获取组件
                depends = self._get_unpackage(next_pkg)
                if not depends:
                    continue
                # 组件找源码包
                depends = [self.provides_source(i).replace("\n", "")
                           for i in depends if self.provides_source(i)]
                if depends:
                    _stack.append(depends)
                else:
                    df_row = self._write_csv_update_df_row(
                        df_row)
                    df_row = df_row[:-1]
                    continue
            else:
                df_row = self._write_csv_update_df_row(df_row,
                                                       pkg=next_pkg)

                continue
        return names_set

    def __create_folder_path(self, folder_path):
        """
        Method to create a folder
        Raises:
            IOError: Failed to create folder
        """

        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

    def write_to_csv(self, packagename, folder_path, project):
        """_summary_

        Args:
            project (_type_): _description_
            packagename (_type_): _description_
            folder_path (_type_): _description_
        """
        self.__create_folder_path(folder_path=folder_path)
        self.parse_unresolvable_package(project)
        csv_path = os.path.join(folder_path, f"{packagename}.csv")
        if os.path.exists(csv_path):
            os.remove(csv_path)
        with open(csv_path, "a+", encoding="utf-8", newline="") as un_csv:
            self.un_csv_writer = csv.writer(un_csv)
            name_set = set()
            for package_name in self.un_packages:
                if packagename == package_name:
                    name_set = self._data_to_csv(package_name, name_set)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Cloud archive file upload")
    parser.add_argument("--project", required=False, type=str,
                        help="Please enter project name")
    parser.add_argument("--pkgname", required=True, type=str, help="Please enter the package name you want to query")
    parser.add_argument("--path", required=False, type=str,
                        help="path to output file")
    args = parser.parse_args()
    if not FOLER_PATH and args.path:
        print("Please configure the path of the output file")
    path = FOLER_PATH or args.path
    project = PROJECT or args.project
    obs = OpensuseObsAPI()
    obs.write_to_csv(packagename=args.pkgname, folder_path=path, project = project)
